package TaskStructure
{
	import MathText.*;
	
	import MathTextModeller.*;
	
	import TaskStructure.LogEvent;
	
	import flash.desktop.IFilePromise;
	import flash.display.MovieClip;
	import flash.events.Event;
	
	/** An object of this class is the main display object for the word problem modelling "game".
	 * The underlying MathText and MathTextModeller packages are imported and used to create the word problem display, and
	 * the modelling functionality, respectively
	 */ 
	public class WordProblemItem extends MovieClip
	{
		// private vars
		private var item_number:int;
		private var textPath:String = "MathText/xmldocs/";
		
		// private objects
		private var optionsXML:XML, itemXML:XML, problemTemplatesXML:XML, contextsXML:XML, imagesXML:XML, problemTemplateXML:XML, contextXML:XML, imagesContextXML:XML, errorsXML:XML;
		private var quantityArray:Array;
		private var wordProblemDisplay:WordProblemDisplay;
		private var modelDisplay:ModelDisplayContainer;
		private var logWriter:LogWriter;
		
		/** Constructor with just this item from the xml flow document, also problem and context xml */
		public function WordProblemItem(logWriter:LogWriter, optionsXML:XML, itemXML:XML, problemTemplatesXML:XML, contextsXML:XML, imagesXML:XML, errorsXML:XML, item_number:int=-1)
		{
			this.logWriter = logWriter;
			this.optionsXML = optionsXML;
			this.itemXML = itemXML;	
			this.item_number = item_number;
			this.problemTemplatesXML = problemTemplatesXML;
			this.contextsXML = contextsXML;
			this.imagesXML = imagesXML;
			this.errorsXML = errorsXML;
			// now get the singular context, problem, etc.
			var problemSetName:String = "default";
			if (itemXML.attribute("problemSet").length()>0) problemSetName = itemXML.attribute("problemSet")[0];
			if (itemXML.attribute("problemTemplate").length()>0 && problemTemplatesXML..set.(attribute("id") == problemSetName)[0]..problem.(attribute("id")==itemXML.attribute("problemTemplate")[0]).length()>0)
			{
				this.problemTemplateXML = problemTemplatesXML..set.(attribute("id") == problemSetName)[0]..problem.(attribute("id")==itemXML.@problemTemplate[0])[0];
			} else
			{
				this.problemTemplateXML = this.getRandomProblem();
			}
			if (itemXML.attribute("contextText").length()>0 && contextsXML..context.(attribute("id")==itemXML.attribute("contextText")[0]).length()>0)
			{
				this.contextXML = contextsXML..context.(attribute("id")==itemXML.@contextText[0])[0];
			} else
			{
				this.contextXML = this.getRandomProblem();
			}
			if (itemXML.attribute("contextImages").length()>0 && imagesXML..context.(attribute("id")==itemXML.attribute("contextImages")[0]).length()>0)
			{
				this.imagesContextXML = imagesXML..context.(attribute("id")==itemXML.@contextImages[0])[0];
			} else
			{
				this.imagesContextXML = this.getRandomProblem();
			}
			quantityArray = getQuantitiesFromXML(itemXML);
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded (event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			this.graphics.drawRect(0, 0, this.parent.width, this.parent.height);
			// add word problem on top
			wordProblemDisplay = new WordProblemDisplay (this.width, this.height/5, problemTemplateXML, contextXML, quantityArray);
			this.addChild(wordProblemDisplay);
			
			
			// add modeller display below problem
			modelDisplay = new ModelDisplayContainer(this.width, this.height*4/5, wordProblemDisplay, optionsXML, imagesContextXML, errorsXML);
			modelDisplay.addEventListener(LogEvent.ANY, handleLogEvent);
			modelDisplay.addEventListener(Event.COMPLETE, handleCompleted);
			modelDisplay.y = wordProblemDisplay.y + wordProblemDisplay.height;
			this.addChild(modelDisplay);
		}
		private function handleCompleted (event:Event):void
		{
			modelDisplay.removeEventListener(Event.COMPLETE, handleCompleted);
			this.dispatchEvent( new Event(Event.COMPLETE));
		}
		/** Dealing with log events */
		private function handleLogEvent (event:LogEvent):void
		{
			//trace("log", event.timestamp, event.type, event.direct_name, event.direct_where, event.direct_x, event.direct_y, event.indirect_name, event.indirect_where, event.indirect_x, event.indirect_y)
			logWriter.addNewLine(String(itemXML.attribute("id")[0] + "," + event.toString()));
		}
		
		
		//////////////////////////// FOR RANDOM PROBLEM TEMPLATE, CONTEXT, OR IMAGES ///////////////////////////////////
		private function getQuantitiesFromXML(itemXML:XML):Array
		{
			var quantityArray:Array = new Array(itemXML..quantity.length()+1);
			// set all values to NaN so we can reference for later
			for (var i:uint=0; i < (quantityArray.length); i++){quantityArray[i] = Number.NaN;}
			var totQuantity:Number = 0;
			var quantity:Number;
			
			var allSet:Boolean = true; // everything will get set on the first pass unless explicitly told not to.
			do
			{
				allSet = true; // reset
				for (i=0; i < (quantityArray.length-1); i++)
				{
					// does this quantity need to be set?
					if (isNaN(quantityArray[i]))
					{
						if (itemXML..quantity[i].toString() != "")
						{
							quantity = Number(itemXML..quantity[i]);
						} else
						{
							var min:Number=1, max:Number=10;
							var tAllSet:Boolean = true;
							if (itemXML..quantity[i].attribute("min").length() > 0)
							{
								var minString:String = itemXML..quantity[i].attribute("min")[0];
								//trace("min reference?", !isNaN(Number(minString)), minString.charAt(0), minString.charAt(1), minString.charAt(1).search(/[1-9]/));
								if (!isNaN(Number(minString)))
								{ // just a pure number
									min = Number(minString);
								} 
								else if (minString.charAt(0)=="q" && minString.charAt(1).search(/[1-9]/) > -1)
								{ // contains reference to another quantity of the form q1, q2, etc. only works 1-9
									var minqref:Number = Number(minString.charAt(1).match(/[1-9]/));
									// do we actually have the value set?
									//trace("isNan", quantityArray[qref-1], !isNaN(quantityArray[qref-1]));
									if (!isNaN(quantityArray[minqref-1]))
									{
										//trace("with +?", minString, minString.search("/\+/"), minString.charAt(2)=="+");
										// is there a + string, meaning that we start from a quantity plus some value?
										if (minString.charAt(2)=="+")
										{
											min = quantityArray[minqref-1]+Number(minString.substr(3).match(/[0-9]+/));
										} else if (minString.charAt(2)=="-")
										{
											min = quantityArray[minqref-1]-Number(minString.substr(3).match(/[0-9]+/));
										} else
										{
											min = quantityArray[minqref-1];
										}
										
									} // no, the value has not been set yet, set the flag to come back to this one
									else
									{
										tAllSet = false;
									}
								}								
							}
							if (itemXML..quantity[i].attribute("max").length() > 0)
							{
								var maxString:String = itemXML..quantity[i].attribute("max")[0];
								//trace("max reference?", !isNaN(Number(maxString)), maxString.charAt(0), maxString.charAt(1), maxString.charAt(1).search(/[1-9]/));
								if (!isNaN(Number(maxString)))
								{ // just a pure number
									max = Number(maxString);
								} 
								else if (maxString.charAt(0)=="q" && maxString.charAt(1).search(/[1-9]/) > -1)
								{ // contains reference to another quantity of the form q1, q2, etc. only works 1-9
									var maxqref:Number = Number(maxString.charAt(1).match(/[1-9]/));
									// do we actually have the value set?
									//trace("isNan", quantityArray[qref-1], !isNaN(quantityArray[qref-1]));
									if (!isNaN(quantityArray[maxqref-1]))
									{
										//trace("with +?", maxString, maxString.search("/\+/"), maxString.charAt(2)=="+");
										// is there a + string, meaning that we start from a quantity plus some value?
										if (maxString.charAt(2)=="+")
										{
											max = quantityArray[maxqref-1]+Number(maxString.substr(3).match(/[0-9]+/));
										} else if (maxString.charAt(2)=="-")
										{
											max = quantityArray[maxqref-1]-Number(maxString.substr(3).match(/[0-9]+/));
										} else
										{
											max = quantityArray[maxqref-1];
										}
										
									} // no, the value has not been set yet, set the flag to come back to this one
									else
									{
										tAllSet = false;
									}
								}								
							}
							if (tAllSet)
							{
								quantity = this.getRandomQuantity(min, max);
							} else
							{
								allSet = false; // we need to come back to this one
							}
						}
						quantityArray[i] = quantity;
						totQuantity += quantity;
					}
				}
			} while (!allSet)
			quantityArray[quantityArray.length-1] = totQuantity;
			return quantityArray;
		}
		
		/** Gets a random problem template */
		private function getRandomProblem():XML
		{
			var problemCount:int = problemTemplatesXML..set.(attribute("id") == "default")[0]..problem.length();
			var randI:int = Math.floor(Math.random()*problemCount);
			return (problemTemplatesXML..set.(attribute("id") == "default")[0]..problem[randI]);
		}
		/** Gets a random context */
		private function getRandomContext():XML
		{
			var contextCount:int = contextsXML..context.length();
			var randI:int = Math.floor(Math.random()*contextCount);
			return (contextsXML..context[randI]);
		}
		private function getRandomQuantity(min:Number=1, max:Number=10):Number
		{
			return Math.floor(Math.random()*(max-min+1)+min);
		}
		
	}
}